package it.antonio.ilpizze.db.index.hash;

import it.antonio.ilpizze.func.ComputeIterator;
import it.antonio.ilpizze.func.Pair;

import java.util.Iterator;

public abstract class HashTable<K, V> implements Iterable<Pair<K,V>>{

	protected abstract int degree();

	protected abstract int hashFunction(K key);

	protected abstract ElementList<K, V> elementList();

	protected abstract Element<K, V> createElement(int hash, K key, V value);

	public void put(K key, V value) {
		int hash = hashFunction(key);
		int index = Math.abs(hash) % degree();

		ElementList<K, V> list = elementList();
		Element<K, V> entry = list.first(index);
		
		if(entry == null){
			list.addFirst(index, createElement(hash, key, value));
			return;
		}
		
		while (entry != null) {
			if (entry.hash() == hash) {
				//update
				entry.value(value); 
				return;
			} else {
				Element<K,V> next = entry.next(); 
				if(next == null){
					entry.next(createElement(hash, key, value));
					return;
				} else {
					entry = next;
				}
			}
		}
		
		
	}

	public V get(K key) {
		int hash = hashFunction(key);
		int index = Math.abs(hash) % degree();

		ElementList<K, V> list = elementList();
		Element<K, V> entry = list.first(index);

		while (entry != null) {
			if (entry.hash() == hash) {
				return entry.value();
			} else {
				entry = entry.next();
			}
		}
		return null;

	}

	public V remove(K key) {
		int hash = hashFunction(key);
		int index = Math.abs(hash) % degree();

		ElementList<K, V> list = elementList();

		Element<K, V> entry = list.first(index);
		Element<K, V> prev = null;
		
		while (entry != null && entry.hash() != hash) {
			prev = entry;
			entry = entry.next();
		}

		if(entry == null){
			return null;
		}
		
		if(entry.hash() == hash) {
			if (prev == null) {
				list.addFirst(index, entry.next());
			} else {
				prev.next(entry.next());
			}

		}
		return entry.value();
	}
	
	@Override
	public Iterator<Pair<K, V>> iterator() {
		return new ComputeIterator<Pair<K,V>>() {
			
			private int index = -1;
			private Element<K,V> current;
			
			@Override
			protected Pair<K, V> computeNext() {
				if(current == null){
					index++;
					
					if(index == degree()){
						return finished();
					}
					
					Element<K, V> e = elementList().first(index);
					if(e == null){
						return computeNext();
					} else {
						current = e;
						return toPair(current);
					}
				} 
				
				current = current.next();
				if(current != null){
					return toPair(current);
				} else {
					return computeNext();
				}
			}
			
			private Pair<K,V> toPair(Element<K,V> e) {
				return Pair.from(e.key(), e.value());
			}
		};
		
		
	}

	
	public StringBuffer dump(StringBuffer buffer) {
		for(int i = 0; i <degree(); i++){
			buffer.append("[ ");
			Element<K, V> e = elementList().first(i);
			while(e != null){
				buffer.append(e.key() + " ");
				e = e.next();
			}
			buffer.append("]\n");
		}
		return buffer;
	}
}
